#!/bin/bash
: <<COMMENT
  Copyright (C) 2012 Tri Le <trile7 at gmail dot com>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation version 3.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
COMMENT

version="findtools v0.3.1"
oIFS=$IFS
blue='\e[0;34m'; green='\e[0;32m'; red='\e[0;31m'; yellow='\e[0;33m'; bold='\e[1m';none='\e[0m'
function menu {
  n=0
  choices=("$@")
  echo -e "$blue$bold$menutitle"
  for i in "${choices[@]}"; do
    echo -e "$none$((n++)))   $yellow$i"
  done
  echo -en "${none}c|q) ${yellow}Cancel"
  echo -e $green
  read -p "Enter a choice from above menu: " i
  echo -e $none
  [[ $i =~ c|q ]] && exit 1
  if test $i -lt $n 2>/dev/null; then
    choice=${choices[i]}; return $i
  else
    echo -e "$red$i is an invalid entry...please try again!"
    menu "${choices[@]}"
  fi
  }

function findmenu {
  if [[ -z $1 ]]; then
    menutitle="---Find Menu---"
    menu "Find file based on name" "Find file based on size" "Find file based on last modified time" "Find empty file or folder" "Find in file content"
    case $choice in
      *name) cmd=byname ;;
      *size) cmd=bysize ;;
      *time) cmd=bytime ;;
      *empty*) cmd=empty ;;
      *content) cmd=infile ;;
    esac
  fi
  $cmd
  findmenu
  }

function actionmenu {
  unset strmenu
  [[ $cmd = byname ]] && strmenu="Change matching pattern to (exact match is required)"
  [[ $cmd = infile ]] && strmenu=( "Change matching pattern to (exact match is required)" "Delete line matching pattern (exact match is required)" )
  menutitle="---What to do with found files?---"
  menu "Copy to" "Copy to - keep directory structure" "Move to" "Delete" "${strmenu[@]}" "Custom command" "Back to find menu"
  case $choice in
    *find*) findmenu ;;
    Copy*structure) copystructure ;;
    Copy*) copy ;;
    Move*) move ;;
    Delete) deletefile ;;
    Change*) change ;;
    Delete?line*) deleteline ;;
    Custom*) customcmd ;;
  esac
  }

function customcmd {
  echo -e "Use ${green}%f$none for found file"
  echo -ne $green
  read -p "Enter custom command: " cstcmd
  echo -e $none
  if [[ $cstcmd ]]; then
    for i in "${files[@]}"; do
      cstcmd1=${cstcmd//%f/\'$i\'}
      eval $cstcmd1
    done
  else
    echo -e "${red}Custom command cannot be emptied"
    customcmd
  fi
  }

function copystructure {
  echo -ne $green
  read -p "Enter destination location: " dest
  echo -e $none
  for i in "${files[@]}"; do
    dir_structure=`dirname ${i#*/}`
    dest2="$dest/$dir_structure"
    mkdir -p $dest2
    cp -vna "$i" "$dest2"
  done
  }

function copy {
  echo -ne $green
  read -p "Enter destination location: " dest
  echo -e $none
  if mkdir -p "$dest"; then
    for i in "${files[@]}"; do
      cp -vna "$i" "$dest"
    done
  else
    echo -e "${red}Cannot create destination folder $none"
    copy
  fi
  }

function move {
  echo -ne $green
  read -p "Enter destination location: " dest
  echo -e $none
  if mkdir -p "$dest"; then
    for i in "${files[@]}"; do
      mv -vn "$i" "$dest"
    done
  else
    echo -e "${red}Cannot create destination folder $none"
    copy
  fi
  }

function deletefile {
  rm -vrf "${files[@]}"
  }

function change {
  echo -e "Leave blank to remove matching pattern."
  echo -ne $green
  read -p "Enter replacement string: " rstring
  echo -e $none
  for i in "${files[@]}"; do
    if [[ $cmd = infile ]]; then
      sed -i "s/$fstring/$rstring/g" "$i"
    else
      newname=${i/$fstring/$rstring}
      mv -vn "$i" "$newname"
    fi
  done
  }

function deleteline {
  for i in "${files[@]}"; do
    sed -i "/$fstring/d" "$i"
  done
  }

function infile {
  unset files
  echo "Find string is case insensitive and can contain basic regex."
  echo -ne $green
  read -p "Enter find string: " fstring
  echo -e $none
  if [[ -z $fstring ]]; then
    echo -e "${red}String cannot be emptied. $none"
    infile
  fi
  echo "Leave file pattern empty to find string in all files"
  echo -ne $green
  read -p "File pattern: " fpattern
  echo -e $none
  tmp=/tmp/foundfiles
  grep -nHIir "$fstring" *"$fpattern"* > $tmp
  while read line; do
    files=( "${files[@]}" "${line%%:*}" )
    echo $line | grep --color=auto -i "$fstring"
  done <$tmp
  rm -f $tmp
  echo
  echo -e "${bold}Found ${#files[@]} files $none"
  [[ $files ]] && actionmenu || infile
  }

function byname {
  echo -e "Use wildcard ${green}?$none for matching any one character, ${green}*$none for matching all characters, and ${green}[]$none for matching ranges of characters enclosed in the brackets.  Find string is case insensitive."
  echo -ne $green
  read -p "Enter find string: " fstring
  echo -ne $none
  if [[ $fstring ]]; then
    IFS=$'\n'
    files=(`find . -iname "*${fstring}*"`)
    IFS=$oIFS
    xstring=${fstring//\*/.\*}
    xstring=${xstring//\?/.}
    xstring=${xstring//./\\.}
    for i in "${files[@]}"; do echo "$i" | grep --color=always -i "$xstring"; done
    echo -e "${bold}Found ${#files[@]} files $none"
    [[ $files ]] && actionmenu || byname
  else
    echo "${red}Find string cannot be emptied"
    byname
  fi
  }

function bysize {
  echo -e "Use ${green}+$none for greater than and ${green}-$none for less than.  Also, use ${green}k M G$none to indicate size in Kilobytes Megabytes Gigabytes, respectively.  For example, find file greater than 50 MB, enter: ${green}+50M"
  read -p "Enter size: " size
  echo -ne $none
  if test ${size%[kMG]} -ge 0 2>/dev/null || test ${size%[kMG]} -le 0 2>/dev/null; then
    IFS=$'\n'
    files=(`find . -size "$size"`)
    IFS=$oIFS
    du -h "${files[@]}"
    echo
    echo -e "${bold}Found ${#files[@]} files $none"
    [[ $files ]] && actionmenu || bysize
  else
    echo -e "$red$size is invalid$none"
    bysize
  fi
  }

function bytime {
  echo -e "Find files modified n day ago.  For example, find file modified a day ago, enter: ${green}1"
  read -p "Enter day: " day
  echo -ne $none
  if test $day -gt 0 2>/dev/null; then
    IFS=$'\n'
    files=(`find . -mindepth 1 -mtime "-$day"`)
    IFS=$oIFS
    for i in "${files[@]}"; do
      i=(`ls --color=always -l "$i"`)
      echo "${i[@]:5}"
    done
    echo
    echo -e "${bold}Found ${#files[@]} files $none"
    [[ $files ]] && actionmenu || bytime
  else
    echo -e "$red$day must be a positive number $none"
    bytime
  fi
  }

function empty {
  IFS=$'\n'
  files=(`find . -empty`)
  IFS=$oIFS
  for i in "${files[@]}"; do echo "$i"; done
  echo
  echo -e "${bold}Found ${#files[@]} files $none"
  [[ $files ]] && actionmenu || findmenu
  }

function usage {
  echo "$version"
  echo -e "${bold}Usage: ${0##*/} [option] $none"
  echo "Options: menu is shown if option is not specified"
  echo "  --byname  find file by string pattern"
  echo "  --bysize  find file by size"
  echo "  --bytime  find file by last modified time"
  echo "  --empty   find empty file/folder"
  echo "  --infile  find file content matching string pattern"
  exit
  }

case $1 in
  -h|--h*) usage ;;
  --*) cmd=${1#--}; shift ;;
esac
findmenu $cmd
